from ctypes import *

import inspect
import stat
import errno
import os
import re

try:
    from pywinfuse import *
except ImportError as e:
    from dokan import *
    from tools import *
    from fuseBase import *
    from fuseOpen import *
    from fuseUnlink import *
    import myWin32file

__version__='0.1'

def feature_assert(section, feature): return True

@property
def FUSE_PYTHON_API_VERSION():
    return __version__.split('.')

def whoami(): return inspect.stack()[1][3]

def whosdaddy(): return inspect.stack()[2][3]

def log(*args, **kwargs): print(whosdaddy(), args, kwargs)

def dbg(*args): return
    # print(whosdaddy())
    # logStr = ''
    # for i in args: logStr += str(i)
    # print(logStr)

def dbgP(*args):
    logStr=''
    for i in args: logStr="{0}{1}:{2}".format(logStr, str(i), str(type(i)))
    print(logStr)

def cre(CreationDisposition):
    if CreationDisposition==myWin32file.CREATE_ALWAYS:
        #print('return 183')
        return myWin32file.ERROR_ALREADY_EXISTS
    if CreationDisposition==myWin32file.OPEN_ALWAYS:
        #print(myWin32file.ERROR_ALREADY_EXISTS)
        return myWin32file.ERROR_ALREADY_EXISTS
    return 0

class Fuse(openSupport, unlinkSupport, writeSupport, fuseBase):
    def translateFileName(self, FileName):
        return FileName.replace('\\', '/')

    def OpenDirectoryFunc(self, FileName, pInfo):
        unixFilename = FileName.replace('\\', '/')
        st=self.getattrWrapper(unixFilename)
        if st!=-errno.ENOENT: return 0
        else: return -myWin32file.ERROR_FILE_NOT_FOUND

    def CleanupFunc(self, pInfo, *args, **kwargs):
        return 0

    def release(self, path, flags): return -errno.ENOSYS

    def CloseFileFunc(self, FileName, pInfo):
        unixFilename=FileName.replace('\\', '/')

        if self.checkError(self.getattrWrapper(unixFilename))!=0:
            return -myWin32file.ERROR_FILE_NOT_FOUND
        return self.checkError(self.release_wrapper(unixFilename, pInfo))

    def ReadFileFunc(self, FileName, Buffer, NumberOfBytesToRead, NumberOfBytesRead, Offset, pInfo):
        unixFilename=FileName.replace('\\', '/')
        if self.getattr(unixFilename).st_mode&stat.S_IFDIR:
            return -myWin32file.ERROR_FILE_NOT_FOUND
        data=self.read_wrapper(unixFilename, NumberOfBytesToRead, Offset, pInfo)
        if data==-errno.ENOENT: return -myWin32file.ERROR_FILE_NOT_FOUND
        if data=='': return -1
        length=len(data)
        memmove(Buffer, data, length)
        setDwordByPoint(NumberOfBytesRead, length)
        return 0

    def FlushFileBuffersFunc(self, pInfo, *args, **kwargs):
        return 0

    def translateModeFromUnix(self, st):
        if st.st_mode&stat.S_IFDIR:
            return myWin32file.FILE_ATTRIBUTE_DIRECTORY
        else: return myWin32file.FILE_ATTRIBUTE_NORMAL

    def GetFileInformationFunc(self, FileName, Buffer, pInfo):
        unixFilename=FileName.replace('\\', '/')
        st=self.getattrWrapper(unixFilename)

        if st!=-errno.ENOENT:
            setDwordByPoint(Buffer, self.translateModeFromUnix(st))
            setFileTimeByPoint(Buffer+4, st.st_ctime)
            setFileTimeByPoint(Buffer+12, st.st_atime)
            setFileTimeByPoint(Buffer+20, st.st_mtime)
            setDwordByPoint(Buffer+28, 0)
            setDwordByPoint(Buffer+32, st.st_size>>32)
            setDwordByPoint(Buffer+36, st.st_size&0xffffffff)
            setDwordByPoint(Buffer+40, st.st_nlink)
            return 0
        return -2

    def FindFilesFunc(self, PathName, PFillFindData, pInfo):
        unixFilename=PathName.replace('\\', '/')
        root=unixFilename=='/'
        offset=0

        for entry in self.readdir(unixFilename, offset):
            if root and entry.getName() in ('.', '..'): continue

            finalPath=os.path.join(PathName, entry.getName())
            unixFinal=finalPath.replace('\\', '/')

            st=self.getattrWrapper(unixFinal)
            if st==-errno.ENOENT: continue

            he=WIN32_FIND_DATAW(
                self.translateModeFromUnix(st),
                FILETIME(*fileTimeParts(st.st_ctime)),
                FILETIME(*fileTimeParts(st.st_atime)),
                FILETIME(*fileTimeParts(st.st_mtime)),
                st.st_size>>32,
                st.st_size&0xffffffff,
                0, 0, '', ''
            )
            setStringByPoint(addressof(he)+44, str(entry.getName()), myWin32file.MAX_PATH)
            PFillFindData(pointer(he), pInfo)
        return 0

    def FindFilesWithPatternFunc(self, PathName, SearchPattern, PFillFindData, pInfo):
        unixFilename=PathName.replace('\\', '/')
        root=unixFilename=='/'
        offset=0

        for entry in self.readdir(unixFilename, offset):
            if root and entry.getName() in ('.', '..'): continue

            regPat=SearchPattern.replace('*', '.*').replace('\\', '\\\\')
            if re.match(regPat, entry.getName())==None: continue

            finalPath=os.path.join(PathName, entry.getName())
            unixFinal=finalPath.replace('\\', '/')

            st=self.getattrWrapper(unixFinal)
            if st==-errno.ENOENT: continue

            he=WIN32_FIND_DATAW(
                self.translateModeFromUnix(st),
                FILETIME(*fileTimeParts(st.st_ctime)),
                FILETIME(*fileTimeParts(st.st_atime)),
                FILETIME(*fileTimeParts(st.st_mtime)),
                st.st_size>>32,
                st.st_size&0xffffffff,
                0, 0, '', ''
            )
            setStringByPoint(addressof(he)+44, str(entry.getName()), myWin32file.MAX_PATH)
            PFillFindData(pointer(he), pInfo)
        return 0

    def SetFileAttributesFunc(self, pathName, attributes, pInfo):
        print("SET FILE ATTRIBUTE", pathName, attributes, pInfo)
        return 0

    def SetFileTimeFunc(self, pathName, created, accessed, modified, pInfo):
        return self.filetimes(
            pathName,
            fileTimeToUnix(created[0]),
            fileTimeToUnix(accessed[0]),
            fileTimeToUnix(modified[0]),
            pInfo
        )

    def LockFileFunc(self, pInfo, *args, **kwargs):
        return 0

    def UnlockFileFunc(self, pInfo, *args, **kwargs):
        return 0

    def GetDiskFreeSpaceFunc(self, pFreeBytesAvailable, pTotalNumberOfBytes, pTotalNumberOfFreeBytes, pInfo):
        try:
            vfs=self.statfs()
            FreeBytesAvailable=vfs.f_bfree*vfs.f_bsize
            TotalNumberOfBytes=vfs.f_blocks*vfs.f_bsize
            TotalNumberOfFreeBytes=vfs.f_bfree*vfs.f_bsize

        except Exception as e:
            FreeBytesAvailable=0x1000000000000
            TotalNumberOfBytes=0x4000000000000  # 256M=256*1024*1024
            TotalNumberOfFreeBytes=0x1000000000000

        setLongLongByPoint(pFreeBytesAvailable, FreeBytesAvailable)
        setLongLongByPoint(pTotalNumberOfBytes, TotalNumberOfBytes)
        setLongLongByPoint(pTotalNumberOfFreeBytes, TotalNumberOfFreeBytes)
        return 0

    def GetVolumeInformationFunc(self, VolumeNameBuffer, VolumeNameSize, VolumeSerialNumber,
                                 MaximumComponentLength, FileSystemFlags, FileSystemNameBuffer,
                                 FileSystemNameSize, pInfo):

        fsname=str(self.fsname)
        memmove(VolumeNameBuffer, fsname, 2*(len(fsname)+1))
        VolumeSerialNumber=0
        MaximumComponentLength=0
        FileSystemFlags=0
        memmove(FileSystemNameBuffer, 'P3WFS', 2*(len('P3WFS')+1))
        return 0

    def UnmountFunc(self, pInfo, *args, **kwargs): return 0

    def checkError(self, ret):
        if (ret!=None) and isinstance(ret, int) and (ret<0):
            return -myWin32file.ERROR_FILE_NOT_FOUND
        else: return 0
